﻿
using Cms.Core.Application.IService.UserInfo;
using Cms.Core.Application.Service.UserInfo.Dto;
using Cms.Core.Data.Model;
using Cms.Core.Data.UnitOfWork;
using Cms.Core.Infrastructure.Log;
using Cms.Core.Infrastructure.Ui;
using System;
using System.Threading.Tasks;
using Cms.Core.Infrastructure.Extension;
using Cms.Core.Infrastructure.Common;
using System.Linq.Expressions;
using System.Linq;
using Cms.Core.Infrastructure.Helps;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using Cms.Core.Infrastructure.Runtime.Session;
using Cms.Core.Application.IService.RoleInfo;
using Cms.Core.Application.IService.UserRoleInfo;

using System.Security.Claims;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Cms.Core.Application.IService.TokenInfo;
using Cms.Core.Application.Service.TokenInfo;
using Microsoft.AspNetCore.Http;
using Cms.Core.Infrastructure.Filter;
using Cms.Core.Infrastructure.AuthHelper;
using Cms.Core.Application.Secutiry.AuthHelper;

namespace Cms.Core.Application.Service.UserInfo
{


    /// <summary>
    /// 用户信息接口
    /// </summary>
    public class UserService : IUserService
    {
        private readonly IRepository<SysUser> _sysUserRepository = null;

        private readonly ILoggerHelper _loggerHelper = null;
        private readonly ICmsSession _session = null;

        private readonly IRoleService _roleService = null;

        private readonly IUserRoleService _userRoleService = null;

        private readonly IUnitOfWork _unitOfWork;

        private readonly IRepository<SysUserRoleMap> _sysUserRoleMapRepository = null;
        private readonly IRepository<SysRole> _sysRoleRepository = null;

        private readonly PermissionRequirement _requirement;

        private readonly ITokenService _tokenService = null;

        private readonly IHttpContextAccessor _httpContextAccessor;


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="sysUserRepository"></param>
        /// <param name="loggerHelper"></param>
        /// <param name="session"></param>
        /// <param name="roleService"></param>
        ///<param name="userRoleService"></param>
        ///<param name="unitOfWork"></param>
        public UserService(IRepository<SysUser> sysUserRepository, ILoggerHelper loggerHelper, ICmsSession session, IRoleService roleService, IUserRoleService userRoleService, IUnitOfWork unitOfWork, IRepository<SysUserRoleMap> sysUserRoleMapRepository, IRepository<SysRole> sysRoleRepository, PermissionRequirement requirement, ITokenService tokenService, IHttpContextAccessor httpContextAccessor)
        {
            this._sysUserRepository = sysUserRepository;
            this._loggerHelper = loggerHelper;
            this._session = session;
            this._roleService = roleService;
            this._userRoleService = userRoleService;
            this._unitOfWork = unitOfWork;
            this._sysUserRoleMapRepository = sysUserRoleMapRepository;
            this._sysRoleRepository = sysRoleRepository;
            this._requirement = requirement;
            this._tokenService = tokenService;
            this._httpContextAccessor = httpContextAccessor;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>

        public async Task<OperationResult> AddOrUpdateUserAsync(UserDto input)
        {

            if (input.Id == Guid.Empty)
            {
                return await AddUserAsync(input);
            }
            else
            {
                return await UpdateUserAsync(input);
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<OperationResult> AddUserAsync(UserDto input)
        {
            _unitOfWork.BeginTransaction();
            input.Id = Guid.NewGuid();
            var userId = _session.UserId;
            var result = await _sysUserRepository.InsertAsync(input, async o =>
               {

                   this.CheckUserIsNullOrEmpty(o.UserName, o.AccountNumber, o.Password);
                   //检查用户
                   this.CheckUserIsExist(o.UserName, o.AccountNumber, o.Id);



                   string paw = string.Format(Keys.passwordKey, input.Password);

                   o.Password = MD5Encrypt.Encrypt(paw);

                   await Task.CompletedTask;

               }, async (dto, u) =>
               {
                   u.CreateUserId = userId;
                   u.ModifiedUserId = userId;
                   await Task.CompletedTask;
                   return u;
               });

            if (result.Successed)
            {

                if (input.RoleIds.IsExist())
                {
                    //var sysUserRoleMaps = input.RoleIds.Select(o => new SysUserRoleMap
                    //{
                    //    RoleId = o,
                    //    UserId = input.Id,
                    //    CreateUserId = userId,
                    //    ModifiedUserId = userId,

                    //}).ToArray();
                    //result = await _sysUserRoleMapRepository.InsertAsync(sysUserRoleMaps);
                    result = await this.BatchAddSysUserRoleMaps(input.RoleIds, input.Id);
                }

            }

            _unitOfWork.Commit();
            return result;
        }


        /// <summary>
        /// 更新用户信息
        /// </summary>
        /// <param name="input">更新实体</param>
        /// <returns></returns>

        public async Task<OperationResult> UpdateUserAsync(UserDto input)
        {
            var userId = _session.UserId;
            _unitOfWork.BeginTransaction();
            var result = await _sysUserRepository.UpdateNotQueryAsync(input, async (dto, user) =>
             {
                 this.CheckUserIsNullOrEmpty(dto.UserName, dto.AccountNumber, dto.Password, false);
                 //检查用户
                 this.CheckUserIsExist(dto.UserName, dto.AccountNumber, dto.Id);

                 await Task.CompletedTask;
             }, async (dto, u) =>
             {
                 //string paw = string.Format(Keys.passwordKey, input.Password);
                 //u.Password = MD5Encrypt.Encrypt(paw);
                 u.ModifiedTime = DateTime.Now;
                 u.ModifiedUserId = userId;
                 await Task.CompletedTask;
                 return u;

             },
              o => o.UserName,
              o => o.ModifiedTime,
              o => o.ModifiedUserId,
              o => o.Remarks,
              o => o.Sex,
              o => o.Age,
              o => o.AccountNumber,
              o => o.State,
              o => o.Icon
            );

            if (result.Successed)
            {
                await this._sysUserRoleMapRepository.SoftDeleteBatchAsync(o => o.UserId == input.Id, m => new SysUserRoleMap()
                {
                    IsDelete = true
                });

                if (input.RoleIds.IsExist())
                {
                    result = await this.BatchAddSysUserRoleMaps(input.RoleIds, input.Id);
                }

            }

            _unitOfWork.Commit();
            return result;
        }

        /// <summary>
        /// 异步批量添加
        /// </summary>
        /// <param name="roleIds"></param>
        /// <param name="userId"></param>
        /// <returns></returns>

        private async Task<OperationResult> BatchAddSysUserRoleMaps(IEnumerable<Guid> roleIds, Guid userId)
        {
            var sysUserRoleMaps = roleIds.Select(o => new SysUserRoleMap
            {
                RoleId = o,
                UserId = userId,
                CreateUserId = userId,
                ModifiedUserId = userId,

            }).ToArray();
            return await _sysUserRoleMapRepository.InsertAsync(sysUserRoleMaps);
        }
        /// <summary>
        /// 得到用户分页信息
        /// </summary>
        /// <param name="inputDto">分页请求数据</param>
        /// <returns></returns>

        public async Task<PageResult> GetUserPageDataAsync(PageInputDto inputDto)
        {
            inputDto.NotNull(nameof(inputDto));
            Expression<Func<SysUser, bool>> filter = FilterHelp.ToFilterExpression<SysUser>(inputDto.Filters); //得到表达式目录树
            filter.NotNull(nameof(filter));

            OrderCondition<SysUser>[] orderCondition = new OrderCondition<SysUser>[] { new OrderCondition<SysUser>(o => o.Id) };
            inputDto.OrderConditions = orderCondition;
            var query = await _sysUserRepository.QueryNotDelete
             .ToPageAsync(filter, inputDto, o => new
             {

                 o.Id,
                 o.Icon,
                 o.ModifiedTime,
                 o.CreateTime,
                 o.UserName,
                 o.Sex,
                 o.State,
                 o.AccountNumber,
                });

            return query.ToPageResult();
        }

        private bool CheckUserIsExist(string userName, string accountNumber, Guid id)
        {
            var isExist = _sysUserRepository.Query(user => (user.UserName == userName || user.AccountNumber == accountNumber) && (user.Id != (id != Guid.Empty ? id : Guid.Empty))).Any();
            if (isExist)
            {
                throw new CustomException("用户名或者账号名已存在,请重新输入");
            }
            return isExist;
        }


        private void CheckUserIsNullOrEmpty(string userName, string accountNumber, string password, bool isCheckPass = true)
        {
            if (userName.IsNullOrWhiteSpace())
            {
                throw new CustomException("用户名不能为空!!");
            }

            if (isCheckPass)
            {
                if (password.IsNullOrWhiteSpace())
                {
                    throw new CustomException("密码不能为空!!");
                }
            }


            if (accountNumber.IsNullOrWhiteSpace())
            {
                throw new CustomException("登录名不能为空!!");
            }

        }

        /// <summary>
        /// 检查登录信息
        /// </summary>
        /// <param name="accountNumber">账号</param>
        /// <param name="password">密码</param>
        /// <param name="isEncryption">是否对密码进行加密</param>
        /// <returns></returns>
        public async Task<OperationResult<TokenResultDto>> VerificationLoginInfo(string accountNumber, string password, bool isEncryption = true)
        {
            try
            {

                if (accountNumber.IsNullOrWhiteSpace())
                {
                    throw new CustomException("登录名不能为空!!");
                }

                if (password.IsNullOrWhiteSpace())
                {
                    throw new CustomException("密码不能为空!!");
                }

                accountNumber = accountNumber.Trim();
                password = password.Trim();
                var userModel = await _sysUserRepository.QueryNotDelete.Where(o => o.AccountNumber == accountNumber).FirstOrDefaultAsync();

                if (userModel.IsNull())
                {

                    return new OperationResult<TokenResultDto>()
                    {
                        Type = OperationResultType.Error,
                        Message = "该用户不存在！"
                    };
                }
                OperationResult<TokenResultDto> result = new OperationResult<TokenResultDto>("登录成功", OperationResultType.Success);

                if (isEncryption)
                {
                    string paw = string.Format(Keys.passwordKey, password);
                    password = MD5Encrypt.Encrypt(paw);
                }


                if (userModel.Password != password)
                {
                    result.Message = "密码有误,请重新输入!!!";
                    result.Type = OperationResultType.Error;

                    return result;
                }

                if (userModel.State == UserState.Disable.AsTo<int>())
                {
                    result.Message = "该用户被禁用,请联系管理员!!!";
                    result.Type = OperationResultType.Error;
                    return result;
                }

                List<string> roleNames = new List<string>();
                if (result.Successed)
                {

                    roleNames = await this._sysUserRoleMapRepository.QueryNotDelete.Where(u => u.UserId == userModel.Id)
                        .Join(this._sysRoleRepository.QueryNotDelete, ur => ur.RoleId, r => r.Id, (ur, r) => r.Name).ToListAsync();

                }

                //result.Data = new LoginUserDto()
                //{
                //    Id = userModel.Id,
                //    NickName= userModel.AccountNumber,
                //    UserName = userModel.UserName,
                //    RoleNames = roleNames

                //};

                var claims = this.BuildClaim(userModel.Id.ToString(), userModel.UserName, roleNames, out DateTime expiration).ToArray();

                var token = JwtToken.BuildJwtToken(claims, _requirement);
                await this._tokenService.SaveTokenAsync(new TokenDto()
                {

                    Token = token.Token,
                    ExpirationTime = expiration,
                    CreateUserId = userModel.Id,
                }, claims);
                result.Data = new TokenResultDto
                {
                    Token = token.Token,
                    Expires_in = token.ExpiresIn,
                    Token_type = token.TokenType,
                    NickName = userModel.UserName,

                };
                return result;
            }
            catch (CustomException cu)
            {
                return new OperationResult<TokenResultDto> { Message = cu.Message, Type = OperationResultType.Error };
            }
            catch (Exception ex)
            {
                _loggerHelper.Error(this, ex);
                return new OperationResult<TokenResultDto> { Message = ex.Message, Type = OperationResultType.Exp };
            }

        }

        /// <summary>
        /// 刷新
        /// </summary>
        /// <returns></returns>
        public async Task<OperationResult<TokenResultDto>> RefreshToken(string token)
        {
            //var userDto = await this.GetUserById(id.AsTo<Guid>());

            var tokenDto = await _tokenService.GetToken(token);
            if (tokenDto.IsDelete)
            {
                throw new CustomException("当前token无效!!!");
            }
            var userDto = await this.GetUserById(tokenDto.CreateUserId.Value);
            var result = await this.VerificationLoginInfo(userDto.AccountNumber, userDto.Password, false);
            return result;
        }


        private IEnumerable<Claim> BuildClaim(string jti, string name, IEnumerable<string> roles, out DateTime expiration)
        {
            jti.NotNullOrEmpty(nameof(jti));
            jti.NotNullOrEmpty(nameof(name));
            roles.NotNull(nameof(roles));

            expiration = DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds);
            var claims = new List<Claim> {
                    new Claim(ClaimTypes.Name, name),
                    new Claim(ClaimTypes.GivenName, name),
                    new Claim(JwtRegisteredClaimNames.Jti,jti),
                    new Claim(ClaimTypes.Expiration, expiration.ToString()),
                    //new Claim("refreshTime",expiration.AsTo<DateTime>().AddMinutes(_requirement.RefreshCoefficients).ToString())
                    };

            claims.AddRange(roles.Select(s => new Claim(ClaimTypes.Role, s)));
            var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
            identity.AddClaims(claims);
            return claims;
        }

        /// <summary>
        /// 根据ID得到用户
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<UserDto> GetUserById(Guid id)
        {

            if (id.IsEmptyOrNull())
            {
                throw new Exception($"该{id}键的不符合!!");
            }
            return await _sysUserRepository.GetDtoAsync<UserDto>(id);

        }

        /// <summary>
        /// 异步加载用户表单信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>

        public async Task<OperationResult<UserDto>> LoadUserInfoAsync(Guid? id)
        {
            var userDto = new UserDto();
            if (!id.IsEmptyOrNullOrHasValue())
            {
                var roleLst = await _roleService.GetRoleListByUserIdAsync(id.Value);
                userDto = await _sysUserRepository.GetDtoAsync<UserDto>(id.Value);
                userDto.Password = string.Empty;
                userDto.RoleIds = roleLst.Select(o => o.Value.AsTo<Guid>());
            }

            return new OperationResult<UserDto>()
            {
                Message = "加载数据成功!!",
                Data = userDto,
                Type = OperationResultType.Success,
            };
        }

        /// <summary>
        /// 异步软删除
        /// </summary>
        /// <param name="id">主键ID</param>
        /// <returns></returns>
        public async Task<OperationResult> SoftDeleteUserAsync(Guid id)
        {
            var isAdmin = await _sysUserRepository.QueryNotDelete.Where(o => o.Id == id && o.UserName == "管理员").FirstOrDefaultAsync();
            if (isAdmin.IsNotNull())
            {
                return new OperationResult() { Message = "此用户是管理员，拥有所有权限，无法正常删除！！", Type = OperationResultType.Error };
            }
            return await _sysUserRepository.SoftDeleteAsync(id);
        }


        /// <summary>
        /// 
        /// </summary>
        public IQueryable<SysUser> Users => _sysUserRepository.QueryNotDelete;

        /// <summary>
        /// 根据名字查询用户
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <returns></returns>
        public async Task<UserDto> FindUserByNameAsync(string userName)
        {
            return (await this.Users.AsNoTracking().Where(o => o.UserName == userName).FirstOrDefaultAsync()).MapTo<UserDto>();
        }


        //public IQueryable<SysUser> GetNotDeleUsers()
        //{
        //    _sysUserRepository.QueryNotDelete();
        //}


    }
}
